package com.yao.wechat.service.impl;

import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yao.common.bean.WechatResp;
import com.yao.common.constant.WechatConst;
import com.yao.common.enums.SysErrorEnum;
import com.yao.common.exception.BusinessException;
import com.yao.common.util.RedisUtils;
import com.yao.wechat.entity.dto.PublicDto;
import com.yao.wechat.mapper.PublicMapper;
import com.yao.wechat.mapper.sql.PublicWrapper;
import com.yao.wechat.service.PublicService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import java.time.LocalDateTime;
import java.util.Optional;

/**
 * 微信公众号表 接口实现
 *
 * @author autoCode
 * @version 1.0.0-SNAPSHOT
 * @date 2020-07-23
 */
@Service
@Slf4j
public class PublicServiceImpl extends ServiceImpl<PublicMapper, PublicDto> implements PublicService {

    @Override
    public String getAccessToken(String originId) {
        return getAccessToken(originId, "", "");
    }

    @Override
    public String getAccessToken(String appId, String appSecret) {
        return getAccessToken("", appId, appSecret);
    }

    @Override
    public String getAccessToken(String originId, String appId, String appSecret) {

        // 查询数据库
        Wrapper<PublicDto> queyr = null;
        if (StrUtil.isNotBlank(originId)) {
            queyr = PublicWrapper.byOriginId(originId);
        } else if (StrUtil.isNotBlank(appId) && StrUtil.isNotBlank(appSecret)) {
            queyr = PublicWrapper.byAppId(appId, appSecret);
        } else {
            log.error("参数都为空，无法查询微信token.");
            throw new BusinessException(SysErrorEnum.PARAMETER_ERROR);
        }

        PublicDto publicDto = Optional.ofNullable(getOne(queyr)).orElseThrow(() -> new BusinessException("查询不到公众号信息"));

        // token未过期
        if (StrUtil.isNotEmpty(publicDto.getToken()) && null != publicDto.getExpTime()
            && LocalDateTime.now().isBefore(publicDto.getExpTime())) {
            return publicDto.getToken();
        }

        // 获取token
        WechatResp.TokenResp tokenResp = this.getAccessTokenFromApi(publicDto.getAppId(), publicDto.getAppSecret());

        // 保存数据库和redis
        return setToken(tokenResp, publicDto.getId(), originId);
    }

    @Override
    public String refreshAccessToken(String originId) {

        if (StrUtil.isBlank(originId)) {
            throw new BusinessException(SysErrorEnum.PARAMETER_ERROR);
        }

        PublicDto publicDto = Optional.ofNullable(getOne(PublicWrapper.byOriginId(originId)))
            .orElseThrow(() -> new BusinessException("查询不到公众号信息"));

        WechatResp.TokenResp tokenResp = getAccessTokenFromApi(publicDto.getAppId(), publicDto.getAppSecret());
        return setToken(tokenResp, publicDto.getId(), originId);
    }

    /**
     * 功能描述 : 从微信api获取token
     * 
     * @param appId
     * @param appSecret
     * @author yaoyuming
     * @date 2020/7/23 0023
     * @return {@link WechatResp.TokenResp}
     */
    private WechatResp.TokenResp getAccessTokenFromApi(String appId, String appSecret) {
        RestTemplate restTemplate = new RestTemplate();

        try {
            return restTemplate.getForObject(StrUtil.format(WechatConst.API_ACCESS_TOKEN, appId, appSecret),
                WechatResp.TokenResp.class);
        } catch (Exception e) {
            log.error("调用微信api服务获取access_token失败.", e);
            throw new BusinessException("调用微信api服务获取access_token失败.");
        }
    }

    /**
     * 功能描述 : 保存token到DB和redis
     * 
     * @param tokenResp
     * @param publicId
     * @param originId
     * @author yaoyuming
     * @date 2020/7/23 0023
     * @return {@link String}
     */
    private String setToken(WechatResp.TokenResp tokenResp, Integer publicId, String originId) {

        String token = tokenResp.getAccess_token();
        PublicDto update = new PublicDto();
        update.setId(publicId);
        update.setToken(token);
        update.setExpTime(LocalDateTime.now().plusSeconds(tokenResp.getExpires_in()));

        this.saveOrUpdate(update);

        RedisUtils.set(originId, token, (tokenResp.getExpires_in() - 150));

        return token;
    }
}
